package com.cdn.website.service.impl;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import com.cdn.common.core.domain.R;
import com.cdn.common.core.exception.ServiceException;
import com.cdn.common.core.utils.SSHUtils;
import com.cdn.nodes.api.RemoteClusterService;
import com.cdn.nodes.api.RemoteIpService;
import com.cdn.nodes.api.RemoteNodeService;
import com.cdn.nodes.api.RemoteSshService;
import com.cdn.nodes.api.model.EdgeSshAuth;
import com.cdn.website.domain.EdgeClustersWebsite;
import com.cdn.website.domain.EdgeWebsiteDomains;
import com.cdn.website.domain.EdgeWebsiteNetwork;
import com.cdn.website.domain.dto.EdgeWebsiteAddDTO;
import com.cdn.website.domain.dto.portParamsDTO;
import com.cdn.website.mapper.EdgeClustersWebsiteMapper;
import com.cdn.website.mapper.EdgeWebsiteDomainsMapper;
import com.cdn.website.mapper.EdgeWebsiteNetworkMapper;
import org.springframework.stereotype.Service;
import com.cdn.website.mapper.EdgeWebsiteMapper;
import com.cdn.website.domain.EdgeWebsite;
import com.cdn.website.service.IEdgeWebsiteService;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

/**
 * 网站信息，用于记录网站的基本信息，包括域名、端口、流量统计、证书等Service业务层处理
 *
 * @author Cinzia
 * @date 2024-12-16
 */
@Service
public class EdgeWebsiteServiceImpl implements IEdgeWebsiteService {
    @Resource
    private EdgeWebsiteMapper edgeWebsiteMapper;
    @Resource
    private EdgeClustersWebsiteMapper edgeClustersWebsiteMapper;
    @Resource
    private EdgeWebsiteNetworkMapper edgeWebsiteNetworkMapper;
    @Resource
    private EdgeWebsiteDomainsMapper edgeWebsiteDomainsMapper;
    @Resource
    private RemoteClusterService remoteClusterService;
    @Resource
    private RemoteSshService remoteSshService;
    @Resource
    private RemoteIpService remoteIpService;
    @Resource
    private RemoteNodeService remoteNodeService;

    private static final ExecutorService executorService = Executors.newCachedThreadPool();  // 创建线程池

    /**
     * 查询网站信息，用于记录网站的基本信息，包括域名、端口、流量统计、证书等
     *
     * @param id 网站信息，用于记录网站的基本信息，包括域名、端口、流量统计、证书等主键
     * @return 网站信息，用于记录网站的基本信息，包括域名、端口、流量统计、证书等
     */
    @Override
    public EdgeWebsite selectEdgeWebsiteById(Long id) {
        return edgeWebsiteMapper.selectEdgeWebsiteById(id);
    }

    /**
     * 查询网站信息，用于记录网站的基本信息，包括域名、端口、流量统计、证书等列表
     *
     * @param edgeWebsite 网站信息，用于记录网站的基本信息，包括域名、端口、流量统计、证书等
     * @return 网站信息，用于记录网站的基本信息，包括域名、端口、流量统计、证书等
     */
    @Override
    public List<EdgeWebsite> selectEdgeWebsiteList(EdgeWebsite edgeWebsite) {
        return edgeWebsiteMapper.selectEdgeWebsiteList(edgeWebsite);
    }

    /**
     * 新增网站信息
     *
     * @param edgeWebsiteAddDTO
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = ServiceException.class)
    public int insertEdgeWebsite(EdgeWebsiteAddDTO edgeWebsiteAddDTO) {
        EdgeWebsite edgeWebsite = createEdgeWebsite(edgeWebsiteAddDTO);

        // 先执行所有必要的同步任务
        int result = insertWebsiteAndDomains(edgeWebsite, edgeWebsiteAddDTO.getDomains());
        if (result <= 0) {
            throw new ServiceException("新增网站信息出错");
        }

        result = insertWebsiteNetworkInfo(edgeWebsite, edgeWebsiteAddDTO.getPortParamsList());
        if (result <= 0) {
            throw new ServiceException("新增网站网络信息出错");
        }

        result = insertWebsiteClusterInfo(edgeWebsite, edgeWebsiteAddDTO.getCluster());
        if (result <= 0) {
            throw new ServiceException("新增网站集群信息出错");
        }

        // 异步执行获取集群 DNS、节点信息、SSH认证等操作
        CompletableFuture<String> clusterDnsFuture = CompletableFuture.supplyAsync(() -> getClusterDns(edgeWebsiteAddDTO.getCluster()), executorService);
        CompletableFuture<Long[]> nodeIdsFuture = CompletableFuture.supplyAsync(() -> getClusterNodeIds(edgeWebsiteAddDTO.getCluster()), executorService);
        CompletableFuture<List<EdgeSshAuth>> sshAuthListFuture = CompletableFuture.supplyAsync(() -> getSshAuthForNodes(nodeIdsFuture.join()), executorService);

        List<Integer> boundPorts = getBoundPorts(edgeWebsiteAddDTO.getPortParamsList());

        // 生成CDN Nginx配置，
        CompletableFuture<Void> nginxConfigFuture = clusterDnsFuture.thenAcceptAsync(clusterDns -> {
            // 生成CDN配置
            String cdnNginxConfig = buildCdnNginxConfig(clusterDns, edgeWebsite.getSourceAddress(), boundPorts);

            // 部署到本地服务器
            deployConfigToLocalServer(cdnNginxConfig, clusterDns);
        }, executorService);

        // 获取 DNS线路与域名的映射关系
        CompletableFuture<Map<String, List<String>>> dnsLineToDomainsMapFuture = CompletableFuture.supplyAsync(() -> getDnsLineToDomainsMapping(edgeWebsiteAddDTO.getDomains()), executorService);

        // 等待所有异步操作完成，并在完成后处理每个节点的配置
        CompletableFuture<Void> processNodesFuture = CompletableFuture.allOf(
                nginxConfigFuture,
                nodeIdsFuture,
                sshAuthListFuture,
                dnsLineToDomainsMapFuture
        ).thenAcceptAsync(v -> {
            // 获取异步结果
            String clusterDns = clusterDnsFuture.join();
            Long[] nodeIds = nodeIdsFuture.join();
            List<EdgeSshAuth> sshAuthList = sshAuthListFuture.join();
            Map<String, List<String>> dnsLineToDomainsMap = dnsLineToDomainsMapFuture.join();

            // 处理每个节点的 Nginx 配置
            processNodeConfigs(nodeIds, sshAuthList, dnsLineToDomainsMap, clusterDns, boundPorts);
        }, executorService);

        // 等待最终的所有操作完成
        processNodesFuture.join();

        return 1;
    }

    private EdgeWebsite createEdgeWebsite(EdgeWebsiteAddDTO edgeWebsiteAddDTO) {
        EdgeWebsite edgeWebsite = new EdgeWebsite();
        edgeWebsite.setName(edgeWebsiteAddDTO.getWebsiteName());
        edgeWebsite.setSourceAddress(edgeWebsiteAddDTO.getOrigin());
        edgeWebsite.setWebsiteType(edgeWebsiteAddDTO.getWebsiteType());

        int result = edgeWebsiteMapper.insertEdgeWebsite(edgeWebsite);
        if (result <= 0) {
            throw new ServiceException("新增网站信息出错");
        }

        return edgeWebsite;
    }

    private int insertWebsiteAndDomains(EdgeWebsite edgeWebsite, List<String> domains) {
        int result = 0;
        for (String domain : domains) {
            EdgeWebsiteDomains edgeWebsiteDomains = new EdgeWebsiteDomains();
            edgeWebsiteDomains.setWebsiteId(edgeWebsite.getId());
            edgeWebsiteDomains.setDomain(domain);

            result = edgeWebsiteDomainsMapper.insertEdgeWebsiteDomains(edgeWebsiteDomains);
            if (result <= 0) {
                throw new ServiceException("新增网站域名信息出错");
            }
        }
        return result;
    }

    private int insertWebsiteNetworkInfo(EdgeWebsite edgeWebsite, List<portParamsDTO> portParamsDTOList) {
        int result = 0;
        for (portParamsDTO dto : portParamsDTOList) {
            EdgeWebsiteNetwork edgeWebsiteNetwork = new EdgeWebsiteNetwork();
            edgeWebsiteNetwork.setWebsiteId(edgeWebsite.getId());

            try {
                Integer port = Integer.parseInt(dto.getPort());
                edgeWebsiteNetwork.setPort(port);
            } catch (Exception e) {
                throw new ServiceException("端口格式错误");
            }

            edgeWebsiteNetwork.setProtocol(dto.getProtocol());
            result = edgeWebsiteNetworkMapper.insertEdgeWebsiteNetwork(edgeWebsiteNetwork);
            if (result <= 0) {
                throw new ServiceException("新增网站网络信息出错");
            }
        }
        return result;
    }

    private int insertWebsiteClusterInfo(EdgeWebsite edgeWebsite, Long clusterId) {
        EdgeClustersWebsite edgeClustersWebsite = new EdgeClustersWebsite();
        edgeClustersWebsite.setWebsiteId(edgeWebsite.getId());
        edgeClustersWebsite.setClusterId(clusterId);

        int result = edgeClustersWebsiteMapper.insertEdgeClustersWebsite(edgeClustersWebsite);
        if (result <= 0) {
            throw new ServiceException("新增网站集群信息出错");
        }
        return result;
    }

    private String getClusterDns(Long clusterId) {
        R<String> clusterData = remoteClusterService.getDnsByClusterId(clusterId);
        return clusterData.getMsg();  // 集群的 DNS 域名
    }

    private List<Integer> getBoundPorts(List<portParamsDTO> portParamsDTOList) {
        List<Integer> boundPorts = new ArrayList<>();
        for (portParamsDTO dto : portParamsDTOList) {
            try {
                boundPorts.add(Integer.parseInt(dto.getPort()));
            } catch (Exception e) {
                throw new ServiceException("端口格式错误");
            }
        }
        return boundPorts;
    }

    private void deployConfigToLocalServer(String cdnNginxConfig, String clusterDns) {
        try {
            // 本地服务器的 Nginx 配置路径
            String cdnNginxConfigFile = "/www/server/panel/vhost/nginx/" + clusterDns + ".conf";
            Files.write(Paths.get(cdnNginxConfigFile), cdnNginxConfig.getBytes(), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);

            // 重载 Nginx 配置 - 本地执行命令
            String reloadCommand = "sudo systemctl reload nginx";
            SSHUtils.executeLocalCommand(reloadCommand);
        } catch (Exception e) {
            throw new ServiceException("CDN Nginx 配置部署失败: " + e.getMessage());
        }
    }

    private Long[] getClusterNodeIds(Long clusterId) {
        R<List<Long>> nodeIdsResult = remoteClusterService.getNodesByClusterId(clusterId);
        return nodeIdsResult.getData().toArray(new Long[0]);
    }

    private List<EdgeSshAuth> getSshAuthForNodes(Long[] nodeIds) {
        R<List<EdgeSshAuth>> sshAuthResult = remoteSshService.getEdgeSshAuth(nodeIds);
        if (sshAuthResult.getData().isEmpty()) {
            throw new ServiceException("获取SSH认证信息失败");
        }
        return sshAuthResult.getData();
    }

    private int processNodeConfigs(Long[] nodeIds, List<EdgeSshAuth> sshAuthList, Map<String, List<String>> dnsLineToDomainsMap, String clusterDns, List<Integer> boundPorts) {
        int result = 0;
        for (Long nodeId : nodeIds) {
            // 获取主机信息
            R<String> hostData = remoteIpService.getIpIdByNodeId(nodeId);
            R<Long> portData = remoteSshService.getPortByNodeId(nodeId);
            EdgeSshAuth sshAuth = sshAuthList.stream()
                    .filter(auth -> auth.getNodeId().equals(nodeId))
                    .findFirst()
                    .orElseThrow(() -> new ServiceException("节点SSH认证信息未找到"));
            R<String> dnsLineData = remoteNodeService.getDnsLineByNodeId(nodeId);

            String ipAddress = hostData.getMsg();
            Long port = portData.getData();
            String sshUser = sshAuth.getSshUser();
            String sshKey = sshAuth.getSshKey();
            String dnsLine = dnsLineData.getMsg();

            // 为当前节点选择与 dnsLine 匹配的域名
            List<String> assignedDomains = dnsLineToDomainsMap.getOrDefault(dnsLine, new ArrayList<>());

            // 构建节点的 Nginx 配置
            String nodeNginxConfig = buildNodeNginxConfig(assignedDomains, clusterDns, boundPorts);

            // 上传配置并重载 Nginx 配置
            try {
                String remoteConfigFile = "/www/server/panel/vhost/nginx/" + dnsLine + ".conf";
                SSHUtils.uploadFile(ipAddress, port.intValue(), sshUser, sshKey, nodeNginxConfig, remoteConfigFile);

                // 重载 Nginx 配置
                String reloadCommand = "sudo systemctl reload nginx";
                SSHUtils.executeSSHCommand(ipAddress, port.intValue(), sshUser, sshKey, reloadCommand);
            } catch (Exception e) {
                throw new ServiceException("Nginx 配置部署失败: " + e.getMessage());
            }
        }
        return result;
    }

    private Map<String, List<String>> getDnsLineToDomainsMapping(List<String> domains) {
        Map<String, List<String>> dnsLineToDomainsMap = new HashMap<>();
        for (String domain : domains) {
            String dnsLine = domain.split("\\.")[0];
            dnsLineToDomainsMap.computeIfAbsent(dnsLine, k -> new ArrayList<>()).add(domain);
        }
        return dnsLineToDomainsMap;
    }

    private String buildCdnNginxConfig(String clusterDns, String sourceAddress, List<Integer> boundPorts) {
        StringBuilder config = new StringBuilder();

        // 遍历绑定的端口，生成对应的 server 块
        for (Integer port : boundPorts) {
            config.append("server {\n");

            // 监听端口
            config.append("    listen ").append(port).append(";\n");

            // 添加 server_name 配置，适配所有绑定的域名
            config.append("    server_name ").append(clusterDns).append(";\n");

            // 配置请求转发到源站
            config.append("    location / {\n");
            config.append("        proxy_pass ").append(sourceAddress).append(";\n");

            // 传递一些常用的头部信息
            config.append("        proxy_set_header Host $host;\n");
            config.append("        proxy_set_header X-Real-IP $remote_addr;\n");
            config.append("        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n");
            config.append("    }\n");
            config.append("}\n\n");
        }

        return config.toString();
    }


    private String buildNodeNginxConfig(List<String> domains, String clusterDns, List<Integer> boundPorts) {
        StringBuilder config = new StringBuilder();

        // 遍历绑定的端口，生成对应的 server 块
        for (Integer port : boundPorts) {
            config.append("server {\n");

            // 监听端口
            config.append("    listen ").append(port).append(";\n");

            // 限制总并发连接数为 50
            config.append("    limit_conn perserver 50;\n");

            // 限制每个连接的带宽为 8MB/s
            config.append("    limit_rate 8m;\n");

            // 添加 server_name 配置，适配所有绑定的域名
            for (String domain : domains) {
                config.append("    server_name ").append(domain).append(";\n");
            }

            // 配置请求转发到上级路由
            config.append("    location / {\n");
            config.append("        proxy_pass http://").append(clusterDns).append(";\n");

            // 传递一些常用的头部信息
            config.append("        proxy_set_header Host $host;\n");
            config.append("        proxy_set_header X-Real-IP $remote_addr;\n");
            config.append("        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n");

            config.append("    }\n");
            config.append("}\n\n");
        }

        return config.toString();
    }


    /**
     * 修改网站信息，用于记录网站的基本信息，包括域名、端口、流量统计、证书等
     *
     * @param edgeWebsite 网站信息，用于记录网站的基本信息，包括域名、端口、流量统计、证书等
     * @return 结果
     */
    @Override
    public int updateEdgeWebsite(EdgeWebsite edgeWebsite) {
        return edgeWebsiteMapper.updateEdgeWebsite(edgeWebsite);
    }

    /**
     * 批量删除网站信息，用于记录网站的基本信息，包括域名、端口、流量统计、证书等
     *
     * @param ids 需要删除的网站信息，用于记录网站的基本信息，包括域名、端口、流量统计、证书等主键
     * @return 结果
     */
    @Override
    public int deleteEdgeWebsiteByIds(Long[] ids) {
        return edgeWebsiteMapper.deleteEdgeWebsiteByIds(ids);
    }

    /**
     * 删除网站信息，用于记录网站的基本信息，包括域名、端口、流量统计、证书等信息
     *
     * @param id 网站信息，用于记录网站的基本信息，包括域名、端口、流量统计、证书等主键
     * @return 结果
     */
    @Override
    public int deleteEdgeWebsiteById(Long id) {
        return edgeWebsiteMapper.deleteEdgeWebsiteById(id);
    }
}
